/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sle_connect_manager.h"
#include "sle_connect_state_machine.h"
#include "sle_hilog.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
// //Device
Device::Device(SleAddress addr) : addr_(addr)
{
    if (stateMachine_ == nullptr) {
        stateMachine_ = std::make_unique<SleConnectStateMachine>(*this);
        stateMachine_->Initialize();
    }
    connTimer_ = std::make_unique<Timer>([&]() {
        stateMachine_->ProcessMessage(SleConnectStateMessage::MSG_CONNECT_TIMEOUT);
    });
}

Device::Device(SleAddress addr, uint16_t connId) : connId_(connId), addr_(addr)
{
    if (stateMachine_ == nullptr) {
        stateMachine_ = std::make_unique<SleConnectStateMachine>(*this);
        stateMachine_->Initialize();
    }
    connTimer_ = std::make_unique<Timer>([&]() {
        stateMachine_->ProcessMessage(SleConnectStateMessage::MSG_CONNECT_TIMEOUT);
    });
}

SleErrorCode SleConnectStateMachine::Initialize()
{
    SLE_LOGD("Initialize In");
    std::unique_ptr<State> idle = std::make_unique<Idle>(*this, STATE_IDLE, device_);
    SLE_ASSERT_AND_RETURN_RET_LOG(idle, SLE_ERR_NOT_READY, "create idle failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(Move(idle), SLE_ERR_NOT_READY, "add idle state failed!");

    std::unique_ptr<State> connecting = std::make_unique<Connecting>(*this, STATE_CONNECTING, device_);
    SLE_ASSERT_AND_RETURN_RET_LOG(connecting, SLE_ERR_NOT_READY, "create connecting failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(Move(connecting), SLE_ERR_NOT_READY, "add connecting state failed!");

    std::unique_ptr<State> connected = std::make_unique<Connected>(*this, STATE_CONNECTED, device_);
    SLE_ASSERT_AND_RETURN_RET_LOG(connected, SLE_ERR_NOT_READY, "create connected failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(Move(connected), SLE_ERR_NOT_READY, "add connected state failed!");

    std::unique_ptr<State> disconnecting = std::make_unique<Disconnecting>(*this, STATE_DISCONNECTING, device_);
    SLE_ASSERT_AND_RETURN_RET_LOG(disconnecting, SLE_ERR_NOT_READY, "create disconnecting failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(Move(disconnecting), SLE_ERR_NOT_READY, "add disconnecting state failed!");

    std::unique_ptr<State> disconnected = std::make_unique<Disconnected>(*this, STATE_DISCONNECTED, device_);
    SLE_ASSERT_AND_RETURN_RET_LOG(disconnected, SLE_ERR_NOT_READY, "create disconnected failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(Move(disconnected), SLE_ERR_NOT_READY, "add disconnected state failed!");

    SLE_ASSERT_AND_RETURN_RET_LOG(InitState(STATE_IDLE), SLE_ERR_NOT_READY, "init connect state failed!");
    return SLE_NO_ERROR;
}

bool Idle::Entry()
{
    SLE_LOGD("Idle state entry");
    return true;
}

void Idle::Exit()
{
    SLE_LOGD("Idle state exit");
}

bool Idle::Dispatch(const Message &msg)
{
    SLE_LOGI("Idle dispatch msg code:%d", msg.what_);
    bool result = false;
    switch (msg.what_) {
        case MSG_CONNECT:
            if (SleConnectManager::GetInstance()->DoConnect(device_) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_CONNECTING);
            } else {
                connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_CONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoConnectComplete(device_, msg) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_CONNECTED);
            } else {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_DISCONNECT:
            result = connStateMachine_.Transition(STATE_DISCONNECTED);
            break;
        case MSG_DISCONNECT_CMP:
        case MSG_CONNECT_TIMEOUT:
        default:
            SLE_LOGD("other msg code:%d", msg.what_);
            break;
    }
    return result;
}

bool Connecting::Entry()
{
    SLE_LOGD("Connecting state entry");
    return true;
}

void Connecting::Exit()
{
    SLE_LOGD("Connecting state exit");
}

bool Connecting::Dispatch(const Message &msg)
{
    SLE_LOGI("Connecting dispatch msg code:%d", msg.what_);
    bool result = false;
    switch (msg.what_) {
        case MSG_CONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoConnectComplete(device_, msg) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_CONNECTED);
            } else {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_DISCONNECT_CMP:
            result = connStateMachine_.Transition(STATE_DISCONNECTED);
            break;
        case MSG_CONNECT_TIMEOUT:
            SLE_LOGE("connection timeout error!"); // 无法cancel连接，重新连接的话也会执行失败，只能等待底层上报断连 状态切换至 已断连
            break;
        case MSG_DISCONNECT: // 连接中执行断连会调用失败
        case MSG_CONNECT:
        default:
            SLE_LOGD("other msg code:%d", msg.what_);
            break;
    }
    return result;
}

bool Connected::Entry()
{
    SLE_LOGD("Connected state entry");
    if (device_.SM()->GetLastState()->Name() == STATE_CONNECTING) {
      SleConnectCbInfo info = {
          .state = SLE_CONNECT_DISCONNECTED_STATE,
          .pairState = device_.GetConnectCbInfo().pairState,
          .reason = SLE_DISCONNECT_WITH_NONE
      };
      device_.SetConnectCbInfo(info);
    }
    SleConnectManager::GetInstance()->OnConnectStateChange(device_, SLE_CONNECT_CONNECTED_STATE);
    return true;
}

void Connected::Exit()
{
    SLE_LOGD("Connected state exit");
}

bool Connected::Dispatch(const Message &msg)
{
    SLE_LOGI("Connected dispatch msg code:%d", msg.what_);
    bool result = false;
    switch (msg.what_) {
        case MSG_DISCONNECT:
            if (SleConnectManager::GetInstance()->DoDisconnect(device_) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_DISCONNECTING);
            }
            break;
        case MSG_DISCONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoDisconnectComplete(device_, msg) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_PARAM_UPDATE_REQ:
            if (SleConnectManager::GetInstance()->DoUpdateConnectParam(device_, msg) == SLE_NO_ERROR) {
                result = true;
            }
            break;
        case MSG_PARAM_UPDATE_RSP:
            SleConnectManager::GetInstance()->OnConnectParamUpdateComplete(device_);
            result = true;
            break;
        case MSG_CONNECT_TIMEOUT:
            SLE_LOGE("connection timeout error!");
            result = true;
            break;
        case MSG_CONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoConnectComplete(device_, msg) != SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_CONNECT:
        default:
            break;
    }
    return result;
}

bool Disconnecting::Entry()
{
    SLE_LOGD("Disconnecting state entry");
    return true;
}

void Disconnecting::Exit()
{
    SLE_LOGD("Disconnecting state exit");
}

bool Disconnecting::Dispatch(const Message &msg)
{
    SLE_LOGI("Disconnecting dispatch msg code:%d", msg.what_);
    bool result = false;
    switch (msg.what_) {
        case MSG_DISCONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoDisconnectComplete(device_, msg) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            } else {
                result = connStateMachine_.Transition(STATE_CONNECTED);
            }
            break;
        case MSG_CONNECT:
        case MSG_CONNECT_CMP:
        case MSG_DISCONNECT:
        case MSG_CONNECT_TIMEOUT:
        default:
            SLE_LOGD("other msg code:%d", msg.what_);
            break;
    }

    return result;
}

bool Disconnected::Entry()
{
    SLE_LOGD("Disconnected state entry");
    SleConnectManager::GetInstance()->OnConnectStateChange(device_, SLE_CONNECT_DISCONNECTED_STATE);
    return true;
}

void Disconnected::Exit()
{
    SLE_LOGD("Disconnected state exit");
}

bool Disconnected::Dispatch(const Message &msg)
{
    SLE_LOGI("Disconnected dispatch msg code:%d", msg.what_);
    bool result = false;
    switch (msg.what_) {
        case MSG_CONNECT:
            if (SleConnectManager::GetInstance()->DoConnect(device_) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_CONNECTING);
            } else {
                connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_CONNECT_CMP:
            if (SleConnectManager::GetInstance()->DoConnectComplete(device_, msg) == SLE_NO_ERROR) {
                result = connStateMachine_.Transition(STATE_CONNECTED);
            } else {
                result = connStateMachine_.Transition(STATE_DISCONNECTED);
            }
            break;
        case MSG_CONNECT_TIMEOUT:
        case MSG_DISCONNECT:
        case MSG_DISCONNECT_CMP:
        default:
            break;
    }
    return result;
}
} // SleStandard
} // NearLink
} // OHOS